import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;

import javax.media.j3d.Alpha;
import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Locale;
import javax.media.j3d.Material;
import javax.media.j3d.Node;
import javax.media.j3d.RotationInterpolator;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.VirtualUniverse;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.geometry.Cone;
import com.sun.j3d.utils.geometry.Cylinder;
import com.sun.j3d.utils.geometry.Primitive;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.Viewer;
import com.sun.j3d.utils.universe.ViewingPlatform;


/**
 * Test GUI of four viewpoints viewing the same scene graph. Run as an
 * application.
 *
 * @author  $Author: weberd $
 * @created  4th November 2002
 * @version $Revision: 1.2 $
 */
public class TestMultiPerspectiveGUI extends Applet
{
  static final int GEN_NORMALS = Primitive.GENERATE_NORMALS;

  public void init ()
  {
    InnerModel model = new InnerModel ();

    InnerView  view1 = new InnerView ();
    InnerView  view2 = new InnerView ();
    InnerView  view3 = new InnerView ();
    InnerView  view4 = new InnerView ();

    // set viewing transforms
    setViewingPlatforms (view2, view3, view4); // view1 is default view

    // Contruct content branch graph(s)
    BranchGroup contentBranch = createSceneGraph ();

    // Set the views' model
    view1.setModel (model);
    view2.setModel (model);
    view3.setModel (model);
    view4.setModel (model);

    // add the content branch into the model
    model.setScene (contentBranch);

    //
    // FINAL - add the canvases to the main panel so that the original
    //         canvas is in the biggest window, and the other three canvases
    //         are in 3 side windows on the right
    this.setLayout (new BorderLayout ());

    JPanel mainPanel = new JPanel (new BorderLayout ());
    JPanel sidePanel = new JPanel (new GridLayout (3, 0, 0, 5));

    mainPanel.add ("Center", view1.getCanvas3D ());
    mainPanel.setBorder (BorderFactory.createEtchedBorder ());

    sidePanel.setPreferredSize (new Dimension (300, 400));
    sidePanel.add (view2.getCanvas3D ());
    sidePanel.add (view3.getCanvas3D ());
    sidePanel.add (view4.getCanvas3D ());

    this.add (mainPanel, BorderLayout.CENTER);
    this.add (sidePanel, BorderLayout.EAST);
  }

  public void setViewingPlatforms (InnerView view2, InnerView view3, InnerView view4)
  {
    // view 2
    ViewingPlatform viewingPlatform2 = view2.getViewingPlatform ();
    Transform3D t3d = new Transform3D ();
    viewingPlatform2.getViewPlatformTransform ().getTransform (t3d);
    t3d.rotX (Math.toRadians (45.0));
    t3d.setTranslation (new Vector3d (0.0, -5.0, 5.0));
    viewingPlatform2.getViewPlatformTransform ().setTransform (t3d);

    // view 3
    ViewingPlatform viewingPlatform3 = view3.getViewingPlatform ();
    t3d = new Transform3D ();
    viewingPlatform3.getViewPlatformTransform ().getTransform (t3d);
    t3d.rotY (Math.toRadians (90.0));
    t3d.setTranslation (new Vector3d (5.0, 0.0, 0.0));
    viewingPlatform3.getViewPlatformTransform ().setTransform (t3d);

    // view4
    ViewingPlatform viewingPlatform4 = view4.getViewingPlatform ();
    t3d = new Transform3D ();
    viewingPlatform4.getViewPlatformTransform ().getTransform (t3d);
    t3d.rotZ (Math.toRadians (90.0));
    t3d.setTranslation (new Vector3d (0.0, 0.0, 5.0));
    viewingPlatform4.getViewPlatformTransform ().setTransform (t3d);
  }

  // creates a scene graph of a rotating colour cube
  private BranchGroup createSceneGraph ()
  {
    BranchGroup    root     = new BranchGroup ();
    TransformGroup objTrans = new TransformGroup ();
    objTrans.setCapability (TransformGroup.ALLOW_TRANSFORM_WRITE);
    root.addChild (objTrans);

    Node cube     = new ColorCube (0.4);
    Node cylinder = new Cylinder (0.03f, 1.6f, GEN_NORMALS, getRedAppearance (root));
    objTrans.addChild (cube);
    objTrans.addChild (cylinder);

    Transform3D yAxis    = new Transform3D ();
    Alpha       rotAlpha = new Alpha (-1, 4000);
    RotationInterpolator rotator = new
      RotationInterpolator (rotAlpha, objTrans, yAxis, 0.0f, (float) Math.PI * 2.0f);

    BoundingSphere bounds = new BoundingSphere (new Point3d (0, 0, 0), 10);
    rotator.setSchedulingBounds (bounds);
    root.addChild (rotator);

    root.compile ();

    return root;
  }

  // alternative scene graph of a merged colour cube and a cone
  private BranchGroup createSceneGraph2 ()
  {
    BranchGroup root = new BranchGroup ();

    Node cone = new Cone (0.6f, 1.5f, GEN_NORMALS, getRedAppearance (root));
    Node cube = new ColorCube (0.4);

    root.addChild (cone);
    root.addChild (cube);

    return root;
  }

  private Appearance getRedAppearance(BranchGroup root)
  {
    addLighting (root);

    Material redProps = new Material ();
    redProps.setAmbientColor (1.0f, 0.0f, 0.0f);

    Appearance appearance = new Appearance ();
    appearance.setMaterial (redProps);
    return appearance;
  }

  protected void addLighting (BranchGroup root)
  {
    BoundingSphere bounds       = new BoundingSphere ();
    Color3f        lightColour  = new Color3f (0.7f, 0.7f, 0.7f);
    AmbientLight   ambientLight = new AmbientLight (lightColour);
    ambientLight.setInfluencingBounds (bounds);
    root.addChild (ambientLight);

    DirectionalLight directionalLight = new DirectionalLight ();
    directionalLight.setColor (lightColour);
    directionalLight.setInfluencingBounds (bounds);
    root.addChild (directionalLight);
  }

  public static void main (String[] argv)
  {
    new MainFrame (new TestMultiPerspectiveGUI (), 800, 600);
  }

  class InnerModel
  {
    VirtualUniverse universe;
    Locale          locale;
    BranchGroup     scene;

    public InnerModel ()
    {
      universe = new VirtualUniverse ();
      locale   = new Locale (universe);
    }

    public void setScene (BranchGroup scene)
    {
      if (this.scene != null)
        locale.removeBranchGraph (this.scene);

      this.scene = scene;
      locale.addBranchGraph (this.scene);
    }

    public void addViewingPlatform (ViewingPlatform viewingPlatform)
    {
      locale.addBranchGraph (viewingPlatform);
    }
  }

  class InnerView
  {
    Canvas3D        canvas;
    Viewer          viewer;
    ViewingPlatform viewingPlatform;
    InnerModel      model;

    public InnerView ()
    {
      canvas          = new Canvas3D (SimpleUniverse.getPreferredConfiguration ());
      viewer          = new Viewer (canvas);
      viewingPlatform = new ViewingPlatform ();

      addOrbitBehaviour (canvas, viewingPlatform);
      viewer.setViewingPlatform (viewingPlatform);
      viewingPlatform.setNominalViewingTransform ();
    }

    public Canvas3D getCanvas3D ()
    {
      return canvas;
    }

    public ViewingPlatform getViewingPlatform ()
    {
      return viewingPlatform;
    }

    public void setModel (InnerModel model)
    {
      this.model = model;
      model.addViewingPlatform (viewingPlatform);
    }

    private void addOrbitBehaviour (Canvas3D canvas,
                                    ViewingPlatform viewingPlatform)
    {
      BoundingSphere bounds = new BoundingSphere (new Point3d (0.0, 0.0, 0.0), 100.0);
      OrbitBehavior  orbit  = new OrbitBehavior (canvas, OrbitBehavior.REVERSE_ALL);
      orbit.setSchedulingBounds (bounds);
      viewingPlatform.setViewPlatformBehavior (orbit);
    }
  }
}
